/**@file
 * This header file is part of the I/O library; it contains the C++ interface
 * for the I/O context.
 *
 * @see lely/io2/ctx.h
 *
 * @copyright 2018-2019 Lely Industries N.V.
 *
 * @author J. S. Seldenthuis <jseldenthuis@lely.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LELY_IO2_CTX_HPP_
#define LELY_IO2_CTX_HPP_

#include <lely/io2/ctx.h>
#include <lely/util/error.hpp>

#include <utility>

namespace lely {
namespace io {

/**
 * The type of event generated by an I/O context before and after a process
 * fork.
 */
enum class ForkEvent {
  /// The event generated before the fork.
  prepare = IO_FORK_PREPARE,
  /// The event generated after the fork in the parent process.
  parent = IO_FORK_PARENT,
  /// The event generated after the fork in the child process.
  child = IO_FORK_CHILD
};

/// A refence to an I/O context. This class is a wrapper around `#io_ctx_t*`.
class ContextBase {
 public:
  explicit ContextBase(io_ctx_t* ctx_) noexcept : ctx(ctx_) {}

  operator io_ctx_t*() const noexcept { return ctx; }

  /// @see io_ctx_insert()
  void
  insert(io_svc& svc) noexcept {
    io_ctx_insert(*this, &svc);
  }

  /// @see io_ctx_remove()
  void
  remove(io_svc& svc) noexcept {
    io_ctx_remove(*this, &svc);
  }

  /// @see io_ctx_notify_fork()
  void
  notify_fork(ForkEvent e, ::std::error_code& ec) noexcept {
    int errsv = get_errc();
    set_errc(0);
    if (!io_ctx_notify_fork(*this, static_cast<io_fork_event>(e)))
      ec.clear();
    else
      ec = util::make_error_code();
    set_errc(errsv);
  }

  /// @see io_ctx_notify_fork()
  void
  notify_fork(ForkEvent e) {
    ::std::error_code ec;
    notify_fork(e, ec);
    if (ec) throw ::std::system_error(ec, "notify_fork");
  }

  /// @see io_ctx_shutdown()
  void
  shutdown() noexcept {
    io_ctx_shutdown(*this);
  }

 protected:
  io_ctx_t* ctx{nullptr};
};

/// An I/O context.
class Context : public ContextBase {
 public:
  /// @see io_ctx_create()
  Context() : ContextBase(io_ctx_create()) {
    if (!ctx) util::throw_errc("Context");
  }

  Context(const Context&) = delete;

  Context(Context&& other) noexcept : ContextBase(other.ctx) {
    other.ctx = nullptr;
  }

  Context& operator=(const Context&) = delete;

  Context&
  operator=(Context&& other) noexcept {
    using ::std::swap;
    swap(ctx, other.ctx);
    return *this;
  }

  /// @see io_ctx_destroy()
  ~Context() { io_ctx_destroy(*this); }
};

}  // namespace io
}  // namespace lely

#endif  // !LELY_IO2_CTX_HPP_
